﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows;

using Microsoft.Win32;

namespace DanielVaughan.Calcium.VSIntegration.Installation
{
	[RunInstaller(true)]
	public partial class VSTemplateInstaller : Installer
	{
		public VSTemplateInstaller()
		{
			InitializeComponent();
		}

		string VSVersion
		{
			get
			{
				return Context.Parameters["VSVersion"];
			}
		}

		public override void Commit(IDictionary savedState)
		{
			base.Commit(savedState);

			//MessageBox.Show("Committing custom action.");
			try
			{
				string installDir = GetInstallDir(VSVersion);
				RunVSInstallTemplates(installDir);
			}
			catch (Exception ex)
			{
				Context.LogMessage(string.Format(
					"Unable to call run install template for VS Version {0} Exception: {1}", VSVersion, ex.ToString()));
			}
		}

		public override void Uninstall(IDictionary savedState)
		{
			base.Uninstall(savedState);
			//MessageBox.Show("Uninstall custom action.");

			string filePartName = GetFilePartName(VSVersion);
			/* HACK: The VS Setup project removes files after this method is called, 
			 * therefore we must remove the file before we call devenv /InstallVsTemplates. */
            try
			{
				/* C# Template Removal */
				string csWinTemplateDir = GetVSProjectTemplatesDir(VSVersion, "CSharp", "Windows");
				string csWinItemTemplatesDir = GetVSItemTemplatesDir(VSVersion, "CSharp", "WPF");
				string fileName = csWinTemplateDir + filePartName + "CalciumCSWinLauncher.zip";
				bool existed = DeleteFileIfExists(fileName);
				fileName = csWinTemplateDir + filePartName + "CalciumCSWinModule.zip";
				existed |= DeleteFileIfExists(fileName);
				fileName = csWinItemTemplatesDir + filePartName + "CalciumCSWinView.zip";
				existed |= DeleteFileIfExists(fileName);

				string pathToCSWebTemplate2008 = GetVSProjectTemplatesDir(VSVersion, "CSharp", "Web");
				fileName = pathToCSWebTemplate2008 + filePartName + "CalciumCSWeb.zip";
				existed |= DeleteFileIfExists(fileName);

				/* VB.NET Template Removal */
				string vbWinTemplateDir = GetVSProjectTemplatesDir(VSVersion, "VisualBasic", "Windows");
				fileName = vbWinTemplateDir + filePartName + "CalciumVBWinLauncher.zip";
				existed |= DeleteFileIfExists(fileName);
				fileName = vbWinTemplateDir + filePartName + "CalciumVBWinModule.zip";
				existed |= DeleteFileIfExists(fileName);

				string pathToVBWebTemplate2008 = GetVSProjectTemplatesDir(VSVersion, "VisualBasic", "Web");
				fileName = pathToVBWebTemplate2008 + filePartName + "CalciumVBWeb.zip";
				existed |= DeleteFileIfExists(fileName);

				if (existed)
				{
					string vsInstallDir = GetInstallDir(VSVersion);
					RunVSInstallTemplates(vsInstallDir);
				}
			}
			catch (Exception ex)
			{
				Context.LogMessage("Unable to call remove VS2008 VS Templates. " + ex.ToString());
			}
		}

		string GetFilePartName(string vsVersion)
		{
			switch (vsVersion)
			{
				case "9.0":
					return "VS2008";
				case "10.0":
					return "VS2010";
				default:
				return "VS" + vsVersion.Replace('.', '_');
			}
		}

		string GetVSProjectTemplatesDir(string version, string language, string projectType)
		{
			string installationDir = GetVSInstallationDir(version);
			string result = string.Format("{0}Common7\\IDE\\ProjectTemplates\\{1}\\{2}\\1033\\", 
				installationDir, language, projectType);
			return result;
		}

		string GetVSItemTemplatesDir(string version, string language, string projectType)
		{
			string installationDir = GetVSInstallationDir(version);
			string result = string.Format("{0}Common7\\IDE\\ItemTemplates\\{1}\\{2}\\1033\\",
				installationDir, language, projectType);
			return result;
		}

		string GetVSInstallationDir(string version)
		{
			RegistryKey regKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\VisualStudio\" + version + @"\Setup\VS");
			try
			{
				string vsInstallationPath = regKey.GetValue("ProductDir").ToString();
				return vsInstallationPath;
			}
			finally
			{
				regKey.Close();
			}
		}

		/// <summary>
		/// Installs the templates by calling the Visual Studio Devenv.exe file with the
		/// argument /installvstemplates 
		/// </summary>
		void RunVSInstallTemplates(string vsinstalldir)
		{
			Context.LogMessage("Running Visual Studio InstallVsTemplates");

			ProcessStartInfo startInfo = new ProcessStartInfo
			                             	{
			                             		FileName = vsinstalldir + "devenv.exe",
			                             		Arguments = "/InstallVsTemplates",
			                             		WorkingDirectory = vsinstalldir,
			                             		CreateNoWindow = true,
			                             		UseShellExecute = false,
			                             		RedirectStandardInput = true,
			                             		RedirectStandardOutput = true,
			                             		RedirectStandardError = true
			                             	};

			Process process = new Process {StartInfo = startInfo};

			bool started = process.Start();

			if (started)
			{
				/* Wait for the Devenv.exe to finish. */
				process.WaitForExit();
			}
			else
			{
				throw new ApplicationException("Unable to start Devenv.exe.");
			}
		}

		string GetInstallDir(string vsversion)
		{
			string vsInstallDir = null;
			RegistryKey vskey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\VisualStudio\" + vsversion);
			if (vskey == null)
			{
				Context.LogMessage("Visual Studio " + vsversion + " not installed (registry)");
			}
			else
			{
				vsInstallDir = vskey.GetValue("InstallDir", "").ToString();
				if (string.IsNullOrEmpty(vsInstallDir))
				{
					Context.LogMessage("Visual Studio " + vsversion + " not installed (installdir)");
				}
			}
			return vsInstallDir;
		}

		static bool DeleteFileIfExists(string file)
		{
			if (File.Exists(file))
			{
				File.Delete(file);
				return true;
			}
			return false;
		}

	}
}
